import 'dart:convert';
import 'dart:developer' as developer;
import 'dart:math';

import 'package:aegis_flutter_sdk/src/model/bean.dart';
import 'package:eventify/eventify.dart';
import 'package:http/http.dart' as http;
import 'package:shared_preferences/shared_preferences.dart';

import 'model/config.dart';
import 'model/log.dart';
import 'model/options.dart';
import 'pipes/format.dart';
import 'pipes/index.dart';
import 'pipes/pv.dart';
import 'pipes/repeat_limit.dart';
import 'pipes/report.dart';
import 'pipes/throttle.dart';
import 'pipes/whitelist.dart';

/// 外部接口
mixin Operator {
  /// 初始化
  void init(Config config) {}

  /// 设置aegis上报配置，可以延后调用
  /// 常见使用场景：初始化aegis时无法获取用户uin,等待获取用户uin后，对aegis配置进行修改
  /// setConfig(uin:'user_uin', ext1:'extra message');
  void setConfig(
      {String? id,
      String? uin,
      String? version,
      String? aid,
      int? networkTypeNum,
      String? ext1,
      String? ext2,
      String? ext3});

  /// 发送日志
  Future<void> send({
    required Options options,
    Function(http.Response rsp)? successCallback,
    Function? failCallback,
  }) async {}

  /// 白名单日志上报，[msg]支持所有primitive type, 支持map，支持list，
  /// 但是不支持用户自定义的object,如传入自定义object，必须在class内提供对应的toJson方法
  /// info('info msg');
  /// info(['info1', 'info2']);
  /// info({'key':'value'});
  void info(dynamic msg);

  /// 全量日志上报，[msg]支持所有primitive type, 支持map，支持list，
  /// 但是不支持用户自定义的object,如传入自定义object，必须在class内提供对应的toJson方法
  /// infoAll('infoAll msg');
  /// infoAll(['infoAll1', 'infoAll2']);
  /// infoAll({'key':'value'});
  void infoAll(dynamic msg);

  /// 自定义错误日志上报，[msg]支持所有primitive type, 支持map，支持list，
  /// 但是不支持用户自定义的object,如传入自定义object，必须在class内提供对应的toJson方法
  /// report('report msg');
  /// report(['report1', 'report2']);
  /// report({'key':'value'});
  void report(dynamic msg);

  /// 错误日志上报，[msg]支持所有primitive type, 支持map，支持list，
  /// 但是不支持用户自定义的object,如传入自定义object，必须在class内提供对应的toJson方法
  /// error('error msg');
  /// error(['error1', 'error2']);
  /// error({'key':'value'});
  void error(dynamic msg);

  /// event日志上报,仅支持string类型上报
  /// event('event name');
  void reportEvent(String eventName);

  /// 自定义测速日志上报，[duration]单位为milliseconds,(不允许上报超过60000ms的日志)
  /// reportTime('customDuration',1000);
  void reportTime(String name, int duration,
      {String? ext1, String? ext2, String? ext3, String? from});

  /// 自定义测速打点上报，需要和[timeEnd]配合使用
  /// time('customTime');
  void time(String key);

  /// 自定义测速打点上报，需要和[time]配合使用
  /// timeEnd('customTime');
  void timeEnd(String key);
}

class Core with Operator {
  Core(this.config) {
    Core.instances.add(this);
    init(config);
    _normalLogPipeline = _createNormalPipeLine();
    _eventPipeline = _createEventPipeline();
    _customTimePipeline = _createTimePipeline();

    lifeCycle.emit('onConfigChange', null, config);
  }

  /// 上报配置
  Config config;

  /// 是否在白名单
  bool isWhiteList = false;

  /// 生命周期，目前只有onConfigChange事件和destroy事件
  EventEmitter lifeCycle = EventEmitter();

  /// 上报携带的k-v数据
  late Bean _bean;
  Bean get bean => _bean;

  /// 可同时存在多个aegis实例
  static List<Core> instances = [];

  /// 普通日志管道
  late Function _normalLogPipeline;

  /// 事件日志管道
  late Function _eventPipeline;

  /// 测速日志管道
  late Function _customTimePipeline;

  /// 自定义测速存储事件的map
  final Map<String, int> _timeMap = {};

  @override
  void init(Config config) {
    _bean = Bean(
      id: config.id,
      uin: config.uin ?? '',
      ext1: config.ext1,
      ext2: config.ext2,
      ext3: config.ext3,
      version: config.version,
      networkType: config.networkType,
    );
    setAid();
    developer.log('init aegis config finish bean val: $_bean',
        name: debugLogType.critical);
  }

  void setAid() async {
    SharedPreferences prefs = await SharedPreferences.getInstance();
    String? aid = prefs.getString('AEGIS_ID');
    if (aid != null) {
      _bean.aid = aid;
      config.setAid(aid);
    } else {
      aid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'
          .replaceAllMapped(RegExp(r"[xy]"), (match) {
        int r = Random().nextInt(16);
        int v = match[0] == 'x' ? r : (r & 0x3) | 0x8;

        return v.toRadixString(16);
      });
      prefs.setString('AEGIS_ID', aid);
      _bean.aid = aid;
      config.setAid(aid);
    }

    developer.log('set aid finish bean val: $_bean',
        name: debugLogType.critical);
  }

  /// info, infoAll, report, error等常规日志管道
  Function _createNormalPipeLine() {
    return createPipeline([
      // 节流
      createThrottlePipe<NormalLog>(this, 5),
      // 格式化msg里的日志信息
      formatNormalLogPipe,
      // 同个错误上报做次数限制
      createErrorLogLimitPipe(this),
      //pv上报
      createPvPipe(this),
      // 白名单过滤，如果用户不在白名单中，将会把API_RESPONSE和INFO两种等级的日志过滤掉
      createWhitelistPipe(this),
      // 上报
      createReportPipe(this),
    ]);
  }

  Function _createEventPipeline() {
    return createPipeline([
      // 节流
      createThrottlePipe<EventLog>(this, 5),
      (List<EventLog> logs, Resolve resolve) {
        try {
          String payload = Uri.encodeComponent(jsonEncode(logs));
          String url = '${config.eventUrl}?payload=$payload';
          send(options: Options(url: url, type: SendType.event));
        } catch (e) {
          print("event url error:$e");
        }
      }
    ]);
  }

  /// 自定义测速管道
  Function _createTimePipeline() {
    return createPipeline([
      createThrottlePipe<ReportTimeLog>(this, 5),
      (List<ReportTimeLog> logs, Resolve resolve) {
        String payload = Uri.encodeComponent(jsonEncode({'custom': logs}));
        String url = '${config.customTimeUrl}?payload=$payload';
        send(options: Options(url: url, type: SendType.custom));
      }
    ]);
  }

  @override
  void setConfig(
      {String? id,
      String? uin,
      String? version,
      String? aid,
      int? networkTypeNum,
      String? ext1,
      String? ext2,
      String? ext3}) {
    config.id = id ?? config.id;
    config.uin = uin ?? config.uin;
    config.aid = aid ?? config.aid;
    config.version = version ?? config.version;
    config.ext1 = ext1 ?? config.ext1;
    config.ext2 = ext2 ?? config.ext2;
    config.ext3 = ext3 ?? config.ext3;
    bool shouldSendWhiteList =
        _bean.id != id || _bean.uin != uin || _bean.aid != aid;
    _bean.modifyValByConfig(config);
    if (shouldSendWhiteList) {
      lifeCycle.emit('onConfigChange', config);
    }
  }

  @override
  Future<void> send({
    required Options options,
    Function(http.Response rsp)? successCallback,
    Function? failCallback,
  }) async {
    if (_bean.id == '') {
      return;
    }
    String url = options.url;
    url = !url.contains('?') ? '$url?$_bean' : '$url&$_bean';

    // 当前只存在POST和GET两种方法
    if (options.method == Method.GET) {
      try {
        http.Response rsp = await http.get(Uri.parse(url));
        if (_httpSuccess(rsp) && successCallback != null) {
          successCallback(rsp);
        }
        if (!_httpSuccess(rsp) && failCallback != null) {
          failCallback(rsp);
        }
      } catch (e) {
        developer.log('aegis send error [get]',
            name: debugLogType.critical, error: e);
      }
    }
    if (options.method == Method.POST) {
      try {
        final rsp = await http.post(Uri.parse(url),
            body: options.data, headers: {'Content-Type': options.contentType});
        if (_httpSuccess(rsp) && successCallback != null) {
          successCallback(rsp);
        }
        if (!_httpSuccess(rsp) && failCallback != null) {
          failCallback(rsp);
        }
      } catch (e) {
        developer.log('aegis send error [post]',
            name: debugLogType.sendError, error: e);
      }
    }
  }

  bool _httpSuccess(http.Response rsp) {
    return (rsp.statusCode / 100).floor() == 2;
  }

  @override
  void info(
    dynamic msg, {
    String? ext1,
    String? ext2,
    String? ext3,
  }) {
    _normalLogPipeline(_buildNormalLog(
      msg,
      LogType.info,
      ext1: ext1 ?? config.ext1,
      ext2: ext2 ?? config.ext2,
      ext3: ext3 ?? config.ext3,
    ));
  }

  @override
  void infoAll(
    dynamic msg, {
    String? ext1,
    String? ext2,
    String? ext3,
  }) {
    try {
      _normalLogPipeline(_buildNormalLog(
        msg,
        LogType.infoAll,
        ext1: ext1 ?? config.ext1,
        ext2: ext2 ?? config.ext2,
        ext3: ext3 ?? config.ext3,
      ));
    } catch (e) {
      developer.log('infoAll error', name: debugLogType.sendError, error: e);
    }
  }

  @override
  void report(
    dynamic msg, {
    String? ext1,
    String? ext2,
    String? ext3,
  }) {
    try {
      _normalLogPipeline(_buildNormalLog(
        msg,
        LogType.report,
        ext1: ext1 ?? config.ext1,
        ext2: ext2 ?? config.ext2,
        ext3: ext3 ?? config.ext3,
      ));
    } catch (e) {
      developer.log('report error', name: debugLogType.sendError, error: e);
    }
  }

  @override
  void error(
    dynamic msg, {
    String? ext1,
    String? ext2,
    String? ext3,
  }) {
    try {
      _normalLogPipeline(_buildNormalLog(
        msg,
        LogType.error,
        ext1: ext1 ?? config.ext1,
        ext2: ext2 ?? config.ext2,
        ext3: ext3 ?? config.ext3,
      ));
    } catch (e) {
      developer.log('[error] error', name: debugLogType.sendError, error: e);
    }
  }

  @override
  void reportEvent(
    String eventName, {
    String? ext1,
    String? ext2,
    String? ext3,
  }) {
    try {
      if (eventName == '') return;
      EventLog eventLog = EventLog(
        name: eventName,
        ext1: ext1 ?? _bean.ext1,
        ext2: ext2 ?? _bean.ext2,
        ext3: ext3 ?? _bean.ext3,
      );
      // 传入必须为数组
      _eventPipeline([eventLog]);
    } catch (e) {
      developer.log('[reportEvent] error',
          name: debugLogType.sendError, error: e);
    }
  }

  @override
  void reportTime(String name, int duration,
      {String? ext1, String? ext2, String? ext3, String? from}) {
    try {
      if (duration < 0 || duration > 60000) {
        developer.log('reportTime duration must between 0 and 60000',
            name: debugLogType.sendError);

        return;
      }
      ReportTimeLog timelog = ReportTimeLog(
        name: name,
        duration: duration,
        ext1: ext1 ?? config.ext1,
        ext2: ext2 ?? config.ext2,
        ext3: ext3 ?? config.ext3,
        from: from,
      );
      _customTimePipeline([timelog]);
    } catch (e) {
      developer.log('reportTime error', name: debugLogType.sendError, error: e);
    }
  }

  @override
  void time(String key) {
    if (_timeMap[key] == null) {
      _timeMap[key] = DateTime.now().millisecondsSinceEpoch;
    } else {
      developer.log('[time] report time key exist',
          name: debugLogType.critical);
    }
  }

  @override
  void timeEnd(String key) {
    if (_timeMap[key] != null && _timeMap[key] is int) {
      int duration = DateTime.now().millisecondsSinceEpoch - _timeMap[key]!;
      reportTime(key, duration);
      _timeMap.remove(key);
    } else {
      developer.log('report time key exist',
          name: debugLogType.critical, error: e);
    }
  }

  /// 所有输入转化为list，方便后续pipe里处理时可以对齐处理方式
  List<NormalLog> _buildNormalLog(dynamic msg, String level,
      {String? ext1, String? ext2, String? ext3}) {
    List<NormalLog> listifiedLog = [
      NormalLog(msg: msg, level: level, ext1: ext1, ext2: ext2, ext3: ext3)
    ];

    return listifiedLog;
  }

  // @override
  // void extendBean(String key, String value) {
  //   bean[key] = value;
  // }

  void destroy() {
    Core.instances.remove(this);
    lifeCycle.emit('destroy');
    lifeCycle.clear();
  }
}
